#include <qDebug>
#include <QSettings>
#include <QMessageBox>
#include <QMapIterator>
#include <QCoreApplication>

#include "iostream"
#include "MDeviceManager.h"
#include "devicenotifier.h"
#include "SerialPorts.h"
#include "LTransliteration.h"

using namespace Mikroakustika::MDeviceChecker;
using namespace std;
const QString ru_loc = "ru_RU";

//----------------------------------------------------------------------------------------
MDeviceManager::MDeviceManager(QObject *parent) :
    QObject(parent) {

    initManager(parent);
}

//----------------------------------------------------------------------------------------
void MDeviceManager::initManager(QObject *parent) {

    connectedDevices.clear();
    availableSerialPorts.clear();

    if(!LoadLibusbFuncFromDLL()) {
        QMessageBox::critical(0,
                              QObject::tr("Error"),
                              QObject::tr("Couldn't load libusb0.dll"),
                              QMessageBox::Ok);
        QCoreApplication::quit();
    }

    usb_init();
    usb_find_busses();

    usbDevHandle = NULL;
    captionOfSerialPort = "";
    isNeedManufacturerConnected = false;

    connect (DeviceNotifier::instance(), SIGNAL(deviceArrival()), this, SLOT(deviceAdd()));
    connect (DeviceNotifier::instance(), SIGNAL(deviceRemove()), this, SLOT(deviceRem()));
    connect (this,
             SIGNAL(refreshDeviceList(connDevicesType&,
                                      avSerPort&,
                                      QString&,
                                      devStatus,
                                      bool)), parent,
             SLOT(reconstructTrayMenu(connDevicesType&,
                                      avSerPort&,
                                      QString&,
                                      devStatus,
                                      bool)));
    connect (this, SIGNAL(sendBaloon(QString&)), parent, SLOT(showMessageTrayBaloon(QString&)));

    scanAvailableSerialPorts(availableSerialPorts);
    scanDevices(connectedDevices);
    strikeOffVirtualPorts(connectedDevices, availableSerialPorts);

    emit refreshDeviceList(connectedDevices, availableSerialPorts, captionOfSerialPort, deviceOnStartApp, isNeedManufacturerConnected);
}

//----------------------------------------------------------------------------------------
QString MDeviceManager::DecToHex(int Value){
    const char HS[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    QString res = "";

    while(Value > 0){
        res = QString(HS[Value % 16]) + res;
        Value = Value >> 4;
    }
    return res;
}

//----------------------------------------------------------------------------------------
void MDeviceManager::scanDevOnBuses(devStatus devStat) {

    connDevicesType newListDev;
    newListDev.clear();
    scanDevices(newListDev);
    QString textBaloon = compareDevLists(connectedDevices, newListDev, devStat);

    connectedDevices.clear();
    connectedDevices = newListDev;

    availableSerialPorts.clear();
    scanAvailableSerialPorts(availableSerialPorts);

    strikeOffVirtualPorts(connectedDevices, availableSerialPorts);

    //    debugPrintDevices(connectedDevices);
    //    debugPrintSerialPorts(availableSerialPorts);

    if(textBaloon.length() != 0) {
        emit sendBaloon(textBaloon);
    }
    emit refreshDeviceList(connectedDevices, availableSerialPorts, captionOfSerialPort, devStat, isNeedManufacturerConnected);
}

//----------------------------------------------------------------------------------------
void MDeviceManager::deviceRem() {
    scanDevOnBuses(deviceWasRemoved);
}

//----------------------------------------------------------------------------------------
void MDeviceManager::deviceAdd() {
    scanDevOnBuses(deviceWasAdded);
}

//----------------------------------------------------------------------------------------
void MDeviceManager::strikeOffVirtualPorts(connDevicesType &usbDevList, avSerPort &serPorts) {
    if( (usbDevList.count() == 0) || (serPorts.count() == 0) )
        return;

    connDevicesType::iterator itUSBDev = usbDevList.begin();
    while (itUSBDev != usbDevList.end()) {
        if(serPorts.contains(itUSBDev.value().serialPort))
            serPorts.removeAll(itUSBDev.value().serialPort);

        ++itUSBDev;
    }
}

//----------------------------------------------------------------------------------------
QString MDeviceManager::compareDevLists(connDevicesType &currentList,
                                        connDevicesType &newList,
                                        devStatus devStatusAction) {

    if(devStatusAction == deviceWasAdded) {
        captionOfSerialPort = "";
        connDevicesType::iterator itNew = newList.begin();
        while(itNew != newList.end()) {

            devBusInf tmp;
            tmp.description = itNew.value().description;
            tmp.devSerial   = itNew.value().devSerial;
            tmp.serialPort  = itNew.value().serialPort;

            captionOfSerialPort = tmp.serialPort;

            if(!currentList.contains(Manufacturer, tmp)) {
                isNeedManufacturerConnected = true;
                itNew.value().checked = true;
                return (tmp.description + tr(" was connected"));
            }

            isNeedManufacturerConnected = false;
            ++itNew;
        }
    }

    if(devStatusAction == deviceWasRemoved) {
        captionOfSerialPort = "";
        connDevicesType::const_iterator itCur = currentList.constBegin();
        while(itCur != currentList.constEnd()) {

            devBusInf tmp;
            tmp.description = itCur.value().description;
            tmp.devSerial   = itCur.value().devSerial;
            tmp.serialPort  = itCur.value().serialPort;

            captionOfSerialPort = tmp.serialPort;

            if(!newList.contains(Manufacturer, tmp)) {
                isNeedManufacturerConnected = true;
                return (tmp.description + tr(" was disconnected"));
            }

            isNeedManufacturerConnected = false;
            ++itCur;
        }
    }

    return "";
}

//----------------------------------------------------------------------------------------
QString MDeviceManager::getPortNameForFTDI(const QString &vid,
                                           const QString &pid,
                                           const QString ftdiSerial) {
    //TODO: check out WinXP Registry
    //TODO: check out on Linux
    //HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\VID_0403+PID_6001+FTSBF1H4A\0000\Device Parameters
    QString findSerialPortInRegistry("");
    findSerialPortInRegistry = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\FTDIBUS\\VID_";
    findSerialPortInRegistry += vid;
    findSerialPortInRegistry += "+PID_";
    findSerialPortInRegistry += pid;
    findSerialPortInRegistry += "+";
    findSerialPortInRegistry += ftdiSerial;
    findSerialPortInRegistry += "A";
    findSerialPortInRegistry += "\\0000\\Device Parameters";

    QSettings registryPortName(findSerialPortInRegistry, QSettings::NativeFormat);

    return (registryPortName.value("PortName").toString());

}

//----------------------------------------------------------------------------------------
void MDeviceManager::scanDevices(connDevicesType &conDev) {

    usb_find_devices();
    int devCount = 0;

    for(bus = usb_get_busses(); bus; bus = bus->next){
        for(dev = bus->devices; dev; dev = dev->next){
            ++devCount;

            if( (DecToHex(dev->descriptor.idVendor).toAscii() == iVendor) &&
                    (DecToHex(dev->descriptor.idProduct).toAscii() == iProduct) ) {

                memset(ManufacturerString, 0, sizeof(ManufacturerString) / sizeof(ManufacturerString[0]));
                memset(ProductString, 0, sizeof(ProductString) / sizeof(ProductString[0]));
                memset(iSerialNumber, 0, sizeof(iSerialNumber) / sizeof(iSerialNumber[0]));

                usbDevHandle = usb_open(dev);
                usb_get_string_simple(usbDevHandle, dev->descriptor.iManufacturer,
                                      ManufacturerString, sizeof(ManufacturerString));

                if(ManufacturerString == Manufacturer) {
                    usb_get_string_simple(usbDevHandle, dev->descriptor.iProduct,
                                          ProductString, sizeof(ProductString));

                    usb_get_string_simple(usbDevHandle, dev->descriptor.iProduct,
                                          ProductString, sizeof(ProductString));

                    usb_get_string_simple(usbDevHandle, dev->descriptor.iSerialNumber,
                                          iSerialNumber, sizeof(iSerialNumber));

                    QString locale = QLocale::system().name();

                    if(locale == ru_loc) {
                        LTransliteration *trans = new LTransliteration();
                        devBusInfTmp.description = QString(trans->createCyrillic(ProductString));
                        delete trans;
                    }
                    else {
                        devBusInfTmp.description = QString(ProductString);
                    }

                    devBusInfTmp.devSerial  = QString(iSerialNumber);
                    devBusInfTmp.serialPort = getPortNameForFTDI(iVendorStr, iProduct, devBusInfTmp.devSerial);
                    devBusInfTmp.checked = false;



                    conDev.insert(ManufacturerString, devBusInfTmp);
                }

                usb_close(usbDevHandle);
            }
        }
    }
}

//----------------------------------------------------------------------------------------
void MDeviceManager::scanAvailableSerialPorts(avSerPort &serPorts) {
    SerialPorts comPort;
    QStringList curList = comPort.getCurrentSerialPortList();

    for(int i = 0; i != curList.count(); i++) {
        serPorts.append(curList[i]);
    }
}

//----------------------------------------------------------------------------------------
void MDeviceManager::debugPrintDevices(const connDevicesType &conDev) const {
    connDevicesType::const_iterator it = conDev.constBegin();
    while (it != conDev.constEnd()) {
        QString tmpKey = it.key();
        QString tmpV1 = it.value().description;
        QString tmpV2 = it.value().devSerial;
        QString tmpV3 = it.value().serialPort;

        qDebug() << tmpKey << ": "
                 << tmpV1 << " - "
                 << tmpV2 << " - "
                 << tmpV3 << endl;
        ++it;
    }
}

//----------------------------------------------------------------------------------------
void MDeviceManager::debugPrintSerialPorts(const avSerPort &serPorts) const {
    avSerPort::const_iterator it = serPorts.constBegin();
    while (it != serPorts.constEnd()) {
        qDebug() << *it << endl;
        ++it;
    }
}

//----------------------------------------------------------------------------------------
